package cn.ac.ict.fpevaluation.evaluation;

import cn.ac.ict.fpevaluation.bean.*;

import java.util.*;

/**
 * Created by wuyouxiong on 2017/3/6.
 * <p>
 * 评估原子轨迹上的Wifi是否变化, Ap是否移动
 * <p>
 * TEST mac : 8ca6df4e8e92
 * 8c:a6:df:4e:8e:92
 * <p>
 * <p>
 * <p>
 * 实验对象: 连续采集的两条轨迹
 * 实验结论: ① 轨迹1采集到的Ap,可能在轨迹2中会丢失
 * ② 一个Ap可能在整条轨迹中都可以检测到,那么
 * ③ 在样本轨迹中可能有的Ap检测不到,也有可能会新增Ap
 * </p>
 */
public class WifiEvaluation {


    private WifiEvaluation() {
    }

    public static class Builder {
        public static WifiEvaluation evaluation = new WifiEvaluation();
    }

    public static WifiEvaluation getInstance() {
        return Builder.evaluation;
    }


    //最小移动米数
    private static final double DEFAULT_MIN_MOVE_DISTANCE = 5.0;
    //步子大小
    private static final double STEP_SIZE = 0.80;

    private static final double DEFAULT_MIN_FADE_VALUE = 20;

    //最小移动步数
    private static final int DEFAULT_MOVE_DISTANCE = (int) (DEFAULT_MIN_MOVE_DISTANCE / STEP_SIZE);


    //wifi最远传播距离
    private static final double WIFI_SPREAD_DISTANCE = 10;

    //如果Ap有效,那么最小能在一定的步数之内都能检测到该Ap信息.
    private static final int WIFI_EFFECTIVE_STEP_COUNT = (int) (WIFI_SPREAD_DISTANCE / STEP_SIZE);


    /**
     * 计算轨迹中的Ap,并根据Mac将Ap分类.
     *
     * @param traj
     * @return
     */
    public Map<String, List<ApWrapped>> atom2TrajWrapped(AtomTrajectoryBean traj) {
        Map<String, List<ApWrapped>> res = new HashMap<>();
        List<StepBean> stepList = traj.getStepList();
        List<List<APBean>> aps = traj.getAps();
        if (stepList.size() > aps.size()) {
            throw new IllegalArgumentException("轨迹中的采集Ap的次数必须和步数一致");
        }


        for (int i = 0; i < stepList.size(); i++) {
            List<APBean> apBeans = aps.get(i);
            for (APBean apBean : apBeans) {
                String mac = apBean.getMAC();
                if (!res.containsKey(mac)) {
                    res.put(mac, new ArrayList<>());
                }
                res.get(mac).add(new ApWrapped(apBean.getRSS(), i + 1));

            }
        }
        return res;
    }

    /**
     * 查找移动了的Ap
     *
     * @param female 母本
     * @param sample 样本
     * @return 移动了的Ap的mac地址列表
     */
    public List<ApResponse> findMovedAps(Map<String, List<ApWrapped>> female, Map<String, List<ApWrapped>> sample) {
        List<ApResponse> res = new ArrayList<>();

        for (String mac : female.keySet()) {

            List<ApWrapped> apWrappeds = sample.get(mac);
            ApResponse apResponse = new ApResponse();
            apResponse.setMac(mac);
            if (isMoved(female.get(mac), apWrappeds, mac, apResponse)) {

            }
            res.add(apResponse);
            sample.remove(mac);
        }

        System.out.println("在样本轨迹中新增以下" + sample.size() + "个Ap:");
        for (String s : sample.keySet()) {
            if (sample.get(s).size() > WIFI_EFFECTIVE_STEP_COUNT)
                System.out.println(s);
        }

        return res;
    }


    /**
     * 判定Ap点是否移动
     *
     * @param female 母本
     * @param sample 样本
     * @return
     */
    private boolean isMoved(List<ApWrapped> female, List<ApWrapped> sample, String mac, ApResponse apResponse) {

        if (female != null && female.size() < WIFI_EFFECTIVE_STEP_COUNT ||
                sample != null && sample.size() < WIFI_EFFECTIVE_STEP_COUNT) {
            apResponse.setMoved(false);
            apResponse.setLog("样本或母本中采集到的该Ap的数量不足");
            return false;
        }

        //样本中没有该Ap信息
        if (sample == null) {
            boolean apLost = female != null && female.size() >= WIFI_EFFECTIVE_STEP_COUNT &&
                    female.size() != female.get(female.size() - 1).getStepCount() - female.get(0).getStepCount() + 1;
            apResponse.setMoved(apLost);
            apResponse.setLog("样本中采集的该Ap的数据为空,无法判别是否移动, 如果Move为true,则证明移除了该Ap");
            return apLost;
        }

        Collections.sort(female, comparatorByStepCount);
        Collections.sort(sample, comparatorByStepCount);
        //保证接受到的Ap是连续的
        if (female.size() != female.get(female.size() - 1).getStepCount() - female.get(0).getStepCount() + 1 ||
                sample.size() != sample.get(sample.size() - 1).getStepCount() - sample.get(0).getStepCount() + 1) {
            apResponse.setMoved(false);
            apResponse.setLog("样本或母本中采集的该Ap的数据是不连续的,判定为异常数据");
            return false;
        }

        Collections.sort(female, comparatorByRss);
        Collections.sort(sample, comparatorByRss);

        //

        if (female.size() > 0 && sample.size() > 0) {
            int distance = Math.abs(sample.get(sample.size() - 1).getStepCount() - female.get(female.size() - 1).getStepCount());
            //样本中接收到的Ap信号强度的变化范围
            double sampleRssFade = Math.abs(sample.get(sample.size() - 1).getRss() - sample.get(0).getRss());
            //母本中接收到的Ap信号强度的变化范围
            double femaleRssFade = Math.abs(female.get(female.size() - 1).getRss() - female.get(0).getRss());
            boolean isMoved = distance >= DEFAULT_MOVE_DISTANCE
                    && sampleRssFade > DEFAULT_MIN_FADE_VALUE
                    && femaleRssFade > DEFAULT_MIN_FADE_VALUE;
            apResponse.setMoved(isMoved);

            apResponse.setLog("Moved " + distance + " steps, about "
                    + distance / STEP_SIZE + " meters."
                    + "SampleRssFade: " + sampleRssFade
                    + "FemaleRssFade: " + femaleRssFade);
            if (!isMoved) {
                apResponse.appendLog(",判定为未移动的原因: 移动距离不足或样本衰减不足或母本衰减不足");
            }
            return isMoved;

            //return distance >= DEFAULT_MOVE_DISTANCE;

        }

        return false;
    }


    private Comparator<ApWrapped> comparatorByRss = (o1, o2) -> (int) (o1.getRss() - o2.getRss());
    private Comparator<ApWrapped> comparatorByStepCount = (o1, o2) -> o1.getStepCount() - o2.getStepCount();
}
